summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/uic/barcode/utils/ECKeyEncoder.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/uic/barcode/utils/ECKeyEncoder.java')
-rw-r--r--src/main/java/org/uic/barcode/utils/ECKeyEncoder.java284
1 files changed, 284 insertions, 0 deletions
diff --git a/src/main/java/org/uic/barcode/utils/ECKeyEncoder.java b/src/main/java/org/uic/barcode/utils/ECKeyEncoder.java
new file mode 100644
index 0000000..96038d3
--- /dev/null
+++ b/src/main/java/org/uic/barcode/utils/ECKeyEncoder.java
@@ -0,0 +1,284 @@
+package org.uic.barcode.utils;
+
+import java.math.BigInteger;
+import java.security.AlgorithmParameters;
+import java.security.KeyFactory;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.interfaces.ECPublicKey;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.EllipticCurve;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+
+
+public class ECKeyEncoder {
+
+
+ private static final byte X962_UNCOMPRESSED_POINT_INDICATOR = 0x04;
+ private static final byte X962_ODD = 0x02;
+ private static final byte X962_EVEN = 0x03;
+
+ public static final String ENCODING_X509 = "X509";
+ public static final String ENCODING_X962_UNCOMPESSED = "X962_UNCOMPRESSED";
+ public static final String ENCODING_X962_COMPRESSED = "X962_COMPRESSED";
+
+ public static PublicKey fromEncoded (byte[] keyBytes, String oid, Provider provider) {
+
+ PublicKey key = null;
+
+
+ String keyAlgName = null;
+ try {
+ keyAlgName = AlgorithmNameResolver.getName(AlgorithmNameResolver.TYPE_KEY_GENERATOR_ALG, oid,provider);
+ } catch (Exception e1) {
+ throw new IllegalArgumentException("algorithm unknown in: " + provider.getName());
+ }
+
+ if (keyAlgName == null || keyAlgName.length() == 0) {
+ throw new IllegalArgumentException("algorithm unknown in: " + provider.getName());
+ }
+
+ //try standard X.509 encoding first
+
+ try {
+
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
+
+ if (keyAlgName != null && keyAlgName.length() > 0) {
+
+ KeyFactory keyFactory = KeyFactory.getInstance(keyAlgName,provider);
+
+ key = keyFactory.generatePublic(keySpec);
+
+ }
+
+ } catch (Exception e) {
+
+ //try next option
+
+ }
+
+ if (key != null) return key;
+
+ if (keyBytes[0] == X962_UNCOMPRESSED_POINT_INDICATOR) {
+
+
+
+ }
+
+ //maybe a compressed X9.62 eliptic key
+ if (keyBytes[0] == X962_ODD || keyBytes[0] == X962_EVEN) {
+
+
+ try {
+
+ //we need to know the curve!
+ String curveName = EllipticCurveNames.getInstance().getName(oid);
+
+ //get the curve parameters
+ AlgorithmParameters parameters = AlgorithmParameters.getInstance(keyAlgName, provider);
+ parameters.init(new ECGenParameterSpec(curveName));
+ ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class);
+ EllipticCurve curve = ecParameters.getCurve();
+
+ //reconstruct the uncompressed version with the curve
+ byte[] uncompressed = decompressPubkey(keyBytes, curve);
+
+ //decode the uncompressed key
+ return fromUncompressedPoint(uncompressed, ecParameters);
+
+ } catch (Exception e) {
+ key = null;
+ // failed
+ }
+
+
+ }
+
+ //try X962 uncompressed
+ if (keyBytes[0] == X962_UNCOMPRESSED_POINT_INDICATOR) {
+
+ try {
+ //we need to know the curve!
+ String curveName = EllipticCurveNames.getInstance().getName(oid);
+
+ //get the curve parameters
+ AlgorithmParameters parameters = AlgorithmParameters.getInstance(keyAlgName, provider);
+ parameters.init(new ECGenParameterSpec(curveName));
+ ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class);
+
+ //decode the uncompressed key
+ return fromUncompressedPoint(keyBytes, ecParameters);
+
+ } catch(Exception e) {
+ //failed
+ }
+ }
+
+
+ throw new IllegalArgumentException("public key format unknown");
+ }
+
+
+
+ public static byte[] getEncoded(PublicKey key, String encoding){
+
+ if (encoding.equals(ENCODING_X509)) {
+ return key.getEncoded();
+ } else if (encoding.equals(ENCODING_X962_UNCOMPESSED)) {
+
+ if (key instanceof ECPublicKey) {
+
+ return toUncompressedPoint((ECPublicKey) key);
+
+ }
+
+ } else if (encoding.equals(ENCODING_X962_COMPRESSED)) {
+
+ if (key instanceof ECPublicKey) {
+
+ ECPoint point = ((ECPublicKey) key).getW();
+
+ byte[] x = toUnsignedBytes(point.getAffineX());
+
+ BigInteger y = point.getAffineY();
+
+ byte[] compressed = new byte[x.length + 1];
+
+ //compression indicator
+ if (y.testBit(0)) {
+ compressed[0] = 0x03;
+ } else {
+ compressed[0] = 0x02;
+ }
+ System.arraycopy(x, 0, compressed, 1, x.length);
+
+ return compressed;
+ }
+ }
+
+ throw new IllegalArgumentException("unknown encoding");
+
+
+ }
+
+
+ private static ECPublicKey fromUncompressedPoint(
+ final byte[] uncompressedPoint, final ECParameterSpec params)
+ throws Exception {
+
+ int offset = 0;
+ if (uncompressedPoint[offset++] != X962_UNCOMPRESSED_POINT_INDICATOR) {
+ throw new IllegalArgumentException(
+ "Invalid uncompressedPoint encoding, no uncompressed point indicator");
+ }
+
+ int keySizeBytes = (params.getOrder().bitLength() + Byte.SIZE - 1)
+ / Byte.SIZE;
+
+ if (uncompressedPoint.length != 1 + 2 * keySizeBytes) {
+ throw new IllegalArgumentException(
+ "Invalid uncompressedPoint encoding, not the correct size");
+ }
+
+ final BigInteger x = new BigInteger(1, Arrays.copyOfRange(
+ uncompressedPoint, offset, offset + keySizeBytes));
+ offset += keySizeBytes;
+ final BigInteger y = new BigInteger(1, Arrays.copyOfRange(
+ uncompressedPoint, offset, offset + keySizeBytes));
+ final ECPoint w = new ECPoint(x, y);
+ final ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(w, params);
+ final KeyFactory keyFactory = KeyFactory.getInstance("EC");
+ return (ECPublicKey) keyFactory.generatePublic(ecPublicKeySpec);
+ }
+
+ private static byte[] toUncompressedPoint(final ECPublicKey publicKey) {
+
+ int keySizeBytes = (publicKey.getParams().getOrder().bitLength() + Byte.SIZE - 1)
+ / Byte.SIZE;
+
+ final byte[] uncompressedPoint = new byte[1 + 2 * keySizeBytes];
+ int offset = 0;
+ uncompressedPoint[offset++] = 0x04;
+
+ final byte[] x = publicKey.getW().getAffineX().toByteArray();
+ if (x.length <= keySizeBytes) {
+ System.arraycopy(x, 0, uncompressedPoint, offset + keySizeBytes
+ - x.length, x.length);
+ } else if (x.length == keySizeBytes + 1 && x[0] == 0) {
+ System.arraycopy(x, 1, uncompressedPoint, offset, keySizeBytes);
+ } else {
+ throw new IllegalStateException("x value is too large");
+ }
+ offset += keySizeBytes;
+
+ final byte[] y = publicKey.getW().getAffineY().toByteArray();
+ if (y.length <= keySizeBytes) {
+ System.arraycopy(y, 0, uncompressedPoint, offset + keySizeBytes
+ - y.length, y.length);
+ } else if (y.length == keySizeBytes + 1 && y[0] == 0) {
+ System.arraycopy(y, 1, uncompressedPoint, offset, keySizeBytes);
+ } else {
+ throw new IllegalStateException("y value is too large");
+ }
+
+ return uncompressedPoint;
+ }
+
+ static final BigInteger MODULUS =
+ new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
+
+ static final BigInteger POW = MODULUS.add(BigInteger.ONE).shiftRight(2);
+
+ // Given a 33-byte compressed public key, this returns a 65-byte uncompressed key.
+ private static byte[] decompressPubkey(byte[] compressedKey, EllipticCurve curve ) {
+
+ // Check array length and type indicator byte
+ if (compressedKey.length != 33 || compressedKey[0] != 2 && compressedKey[0] != 3)
+ throw new IllegalArgumentException();
+
+ final byte[] xCoordBytes = Arrays.copyOfRange(compressedKey, 1, compressedKey.length);
+ final BigInteger xCoord = new BigInteger(1, xCoordBytes); // Range [0, 2^256)
+
+
+ BigInteger temp = xCoord.pow(2).add(curve.getA());
+ temp = temp.multiply(xCoord);
+ temp = temp.add(curve.getB());
+ temp = temp.modPow(POW, MODULUS);
+
+ //temp = sqrtMod(temp.add(curveParamB));
+
+ boolean tempIsOdd = temp.testBit(0);
+ boolean yShouldBeOdd = compressedKey[0] == 3;
+ if (tempIsOdd != yShouldBeOdd)
+ temp = temp.negate().mod(MODULUS);
+ final BigInteger yCoord = temp;
+
+ // Copy the x coordinate into the new
+ // uncompressed key, and change the type byte
+ byte[] result = Arrays.copyOf(compressedKey, 65);
+ result[0] = 0x04;
+
+ // Carefully copy the y coordinate into uncompressed key
+ final byte[] yCoordBytes = yCoord.toByteArray();
+ for (int i = 0; i < 32 && i < yCoordBytes.length; i++)
+ result[result.length - 1 - i] = yCoordBytes[yCoordBytes.length - 1 - i];
+
+ return result;
+ }
+
+ private static byte[] toUnsignedBytes(BigInteger i) {
+ byte[] b = i.abs().toByteArray();
+ //remove top sign bit
+ if (b[0] == 0) {
+ b = Arrays.copyOfRange(b, 1, b.length);
+ }
+ return b;
+ }
+
+
+}